/* 
 * File:   Streamlines.cpp
 * Author: mvdr
 * 
 * Created on January 19, 2013, 2:13 PM
 */

#include "Streamlines.h"
#include "GL/gl.h"
#include <algorithm>
#include <string>
#include "Utilities.h"

using namespace std;

Streamlines::Streamlines()
:   
    d_maxTime(1000000),
    d_length(10),
    d_time(1000.0),
    d_seeds(0)
{
    d_uses_colormap = true;
    d_uses_vector_dataset = true;
    d_current_map_dataset_index = int(V);
    d_current_vector_dataset_index = int(Flow);    
    d_name = "Streamlines";
    d_legend.setName("Streaml.");
    d_scale = 1.0;
}

void Streamlines::draw(float winW, float winH, int t, int slices, int slice_dt)
{
    const int DIM = d_simulation->getDim();
    fftw_real  wn = (fftw_real)winW / (fftw_real)(DIM + 1);  // Grid cell width
    fftw_real  hn = (fftw_real)winH / (fftw_real)(DIM + 1);  // Grid cell heigh
    
    setColormapMinMax(slices, slice_dt);
    
    for (auto itr = d_seeds.begin(); itr != d_seeds.end(); ++itr)
    {
        drawFromSeed(itr->first, wn, hn, t);  
        itr->second += 1;        
    }
    
    removeSeeds();        
}

void Streamlines::drawFromSeed(Vector2f const &vec, double wn, double hn, int t)
{
    Vector2f curPos = vec;
    int segments = 0;
    float time = 0.0;
    d_map.set(d_map.usedMax(), 1.0);
    float halfWidth = 0.2;
    glBegin(GL_QUADS);
    glVertex2f(wn + (curPos.x - halfWidth) * wn, hn + curPos.y * hn - halfWidth * wn);
    glVertex2f(wn + (curPos.x - halfWidth) * wn, hn + curPos.y * hn + halfWidth * wn);
    glVertex2f(wn + (curPos.x + halfWidth) * wn, hn + curPos.y * hn + halfWidth * wn);
    glVertex2f(wn + (curPos.x + halfWidth) * wn, hn + curPos.y * hn - halfWidth * wn);
    glEnd();
    
    while (segments < d_length && time < d_time)       
    {
        Vector2f v = d_vector_dataset->getData(curPos.x, curPos.y, t);
        if (v.length() > 0)
            v.normalize();
     
        Vector2f newPos = curPos + v * d_scale;
        bool flipped = false;  
        
        
        setLimits(&newPos, &flipped);
        
        if (not flipped)
        {
            d_map.set(d_map_dataset->getData(curPos.x, curPos.y, t), 1.0);
                    
                    //totalAlpha(curPos.x, curPos.y, t));

            glBegin(GL_LINES);
                glVertex2f(wn + curPos.x * wn, hn + curPos.y * hn);
                glVertex2f(wn + newPos.x * wn, hn + newPos.y * hn);
            glEnd();    
        }
        
        curPos = newPos;
        
        ++segments;
        //time += d_scale;
    }       
}

void Streamlines::removeSeeds()
{
    for (auto itr = d_seeds.begin(); itr != d_seeds.end(); ++itr)
    {        
        if (itr->second >= d_maxTime)
        {
            d_seeds.erase(itr);
            --itr;
        }
    }
}

void Streamlines::removeAllSeeds()
{
    d_seeds.clear();
}

void Streamlines::setLimits(Vector2f *vec, bool *changed)
{
    float dim = d_simulation->getDim();
    while (vec->x < 0.0)
    {
        vec->x += dim;
        *changed = true;
    }
    while (vec->y < 0.0)
    {
        vec->y += dim;
        *changed = true;
    }
    while (vec->x > dim)
    {
        vec->x -= dim;
        *changed = true;
    }
    while (vec->y > dim)
    {
        vec->y -= dim;
        *changed = true;
    }
    
}

bool Streamlines::outOfBounds(Vector2f const &vec)
{
    float dim = d_simulation->getDim();
    return vec.x < 0.0 && vec.y < 0.0 && vec.x > dim && vec.y > dim;
}

void Streamlines::addSeed(float x, float y)
{
    d_seeds.push_back(pair<Vector2f, int>(Vector2f(x, y), 0));
}


void Streamlines::seedGrid()
{
    double dx = (d_simulation->getDim()) / (double)d_xGridSize;
    double dy = (d_simulation->getDim()) / (double)d_yGridSize;
    
    float px, py;
    for (size_t x = 0; x < d_xGridSize; x++)
    {
        for (size_t y = 0; y < d_yGridSize; y++)
        {
            px = (x)*dx;
            py = (y)*dy;
            addSeed(px,py);
        }
    }
}